<template>
  <div class="layout-content w-full relative" @mousedown.left="mousedown" @wheel="wheel">
    <div :style="style" class="canvas relative">
      <!--  -->
      <div v-show="currentStage.showRule">
        <HorizontalRule :width="currentStage.w" />
        <VerticalRule :height="currentStage.h" />
      </div>
      <!-- 边界出问题了，来看这里 -->
      <DragVue :key="updateStage" />

      <!-- 框选区域 -->
      <div
        v-show="selectionConfig.show"
        ref="selection"
        class="selection"
        :style="selectionStyle"
      ></div>

      <!-- 拖拽容器，当框选中多个元素后显示 -->
      <div
        v-show="dragBoxConfig.show"
        :style="containerStyle"
        class="drag-container"
        @mousedown="containerMousedown"
      ></div>
    </div>
    <!-- <ShapeTools /> -->
    <Split></Split>
    <FileSelector
      v-model:visible="fileSelectorConfig.show"
      :type="fileSelectorConfig.type"
      :limit="1"
      @change="setMediaSrc"
    />
  </div>
</template>

<script setup lang="ts">
import { IShapeProps } from "@/interface/IShape";
import { findShapeById } from "@/store/events/shape.actions";
import { collide, range } from "@/utils/utils";
import FileSelector from "@/views/admin/components/file-selector/FileSelector.vue";
import "@/views/paint/modules/data/event.keydown";
import {
  dragBoxConfig,
  mouseDirection,
  multipleSelectMode,
  selectSet
} from "@/views/paint/modules/data/shapes";
import {
  currentStage,
  offsetDom,
  setChildCenter,
  setStageMode,
  toggleHandle,
  updateStage
} from "@/views/paint/modules/data/stage";

import DragVue from "./DragVue.vue";
import HorizontalRule from "./rules/HorizontalRule.vue";
import VerticalRule from "./rules/VerticalRule.vue";
import { fileSelectorConfig, setMediaSrc } from "./shape-config/image.event";
import Split from "./split/Split.vue";

const offset = ref({
  ox: 0,
  oy: 0
});

/* 框选的dom */
const selection = ref();

/* 框选样式 */
const selectionConfig = ref({
  left: 0,
  top: 0,
  width: 0,
  height: 0,
  show: false
});

/**
 * 注释: 当键盘按下的时候才能拖拽
 *
 * */

const keydown = (e: KeyboardEvent) => {
  if (e.key === " ") {
    currentStage.value.draggable = true;
  }
};

const keyup = (e: KeyboardEvent) => {
  if (e.key === " ") {
    currentStage.value.draggable = false;
  }
};

/**
 * 注释: 1. 滚动放大
 *       2. 控制手柄大小
 *
 * */

const wheel = (e: WheelEvent) => {
  e.preventDefault();

  dragBoxConfig.value.show = false;
  if (e.deltaY < 0) {
    if (currentStage.value.scale < 3) {
      currentStage.value.scale += 0.05;
    } else {
      currentStage.value.scale = 3;
    }
  } else if (currentStage.value.scale > 0.5) {
    currentStage.value.scale -= 0.05;
  }
  offset.value.ox = e.x;
  offset.value.oy = e.y;

  // setChildCenter();
};

/* 初始化居中，监听键盘事件 */
onMounted(() => {
  /* 设置父容器的基本信息 */
  setChildCenter();
  window.addEventListener("keydown", keydown);
  window.addEventListener("keyup", keyup);
});
/* 移除事件 */
onUnmounted(() => {
  window.removeEventListener("keydown", keydown);
  window.removeEventListener("keydown", keyup);
});

/**
 * 注释: 框选逻辑
 *
 * */
const mousedown = (event: MouseEvent) => {
  toggleHandle(true);
  /* 释放手柄 */
  const ox = event.x - offsetDom.value.left;
  const oy = event.y - offsetDom.value.top;
  const target = event.target as HTMLElement;

  const isInTheDragBox = target.closest("[data-shape]");
  const allowToSelect = target.classList.contains("drag-container");
  if (!isInTheDragBox) {
    /* 当点击的不是组件，设成画布模式 */
    setStageMode();
  }
  /* 如果是点在图形里面就取消框选 */
  if (!currentStage.value.draggable) {
    /* 框选逻辑 */
    multipleSelectMode.value = true; // 打开多选,鼠标抬起关闭多选

    /* 当没有点击到框选容器上，就关闭框选容器 */
    if (!allowToSelect) {
      dragBoxConfig.value.show = false;
      selectionConfig.value.left = event.x;
      selectionConfig.value.top = event.y;
      if (!isInTheDragBox) {
        selectSet.value.clear();
      }
    }
  }
  /* 鼠标拖拽 */
  const mousemove = (evt: MouseEvent) => {
    const mx = Math.abs(evt.x - event.x);
    const my = Math.abs(evt.y - event.y);
    /* shift水平拖拽 */
    if (my > 10 && mx < 5) {
      mouseDirection.value = true;
    } else if (mx > 10 && my < 5) {
      mouseDirection.value = false;
    }

    /* 当鼠标不在组件中点击才能进行框选 */
    if (!isInTheDragBox && !allowToSelect && selectionConfig.value.width > 5) {
      selectionConfig.value.show = true;
      toggleHandle(false);
    }

    /* 整个画布拖拽的逻辑 */
    if (currentStage.value.draggable) {
      offsetDom.value.top = evt.y - oy;
      offsetDom.value.left = evt.x - ox;
    } else {
      /* 框选的逻辑 */
      const offsetX = evt.x - event.x;
      const offsetY = evt.y - event.y;
      if (offsetX > 0) {
        selectionConfig.value.width = evt.x - event.x;
        selectionConfig.value.left = event.x;
      } else {
        selectionConfig.value.left = evt.x;
        selectionConfig.value.width = Math.abs(evt.x - event.x);
      }
      if (offsetY > 0) {
        /* 很关键的一步，当从上面滑动到下面时，坐标发生改变,需要把坐标重置到原点
           利用闭包的特性，可以直接访问鼠标按下的坐标原点
           x，y轴都需要同样的处理
           否则高速移动出现坐标不一致
        */
        selectionConfig.value.top = event.y;
        selectionConfig.value.height = evt.y - event.y;
      } else {
        selectionConfig.value.top = evt.y;
        selectionConfig.value.height = Math.abs(evt.y - event.y);
      }

      const list = document.querySelectorAll("[data-id]");
      const selectionDom = selection.value as HTMLDivElement;
      const { scale } = currentStage.value;
      Array.from(list).forEach((dom) => {
        const { x, y, width, height } = dom.getBoundingClientRect();
        const { x: x1, y: y1, width: w1, height: h1 } = selectionDom.getBoundingClientRect();
        const subRect = {
          x: scale * x,
          y: scale * y,
          width: scale * width,
          height: scale * height
        };
        const selectionRect = {
          x: scale * x1,
          y: scale * y1,
          width: scale * w1,
          height: scale * h1
        };
        /* 碰撞检测，如果碰到了，就加入到选中的集合中 */
        const isCollision = collide(subRect, selectionRect);
        /* 当检测到冲突 */
        const id = dom.getAttribute("data-id");

        if (!id) return;
        if (isCollision) {
          selectSet.value.add(id);
          // current.hiddenHandle = false;
        } else {
          /* 保留位置 */
          // selectSet.value.delete(id);
        }
      });
    }
  };
  const mouseup = () => {
    /* 清空宽高，不然下次点击会显示出来 */
    selectionConfig.value.width = 0;
    selectionConfig.value.height = 0;
    selectionConfig.value.show = false; /* 隐藏框选框 */
    multipleSelectMode.value = false; /* 释放多选 */

    const selectItems = Array.from(selectSet.value);
    const attrList: Array<IShapeProps> = [];
    selectItems.forEach((id) => {
      const shape = findShapeById(id);
      if (!shape) return;
      attrList.push(shape);
    });
    const { scale } = currentStage.value;
    /* 获取所有的属性 */
    const minXSet = attrList.reduce((arr, item) => {
      arr.push(item.x * scale);
      return arr;
    }, [] as any);
    const maxXSet = attrList.reduce((arr, item) => {
      arr.push((item.x + item.w) * scale);
      return arr;
    }, [] as any);
    const minYSet = attrList.reduce((arr, item) => {
      arr.push(item.y * scale);
      return arr;
    }, [] as any);
    const maxYSet = attrList.reduce((arr, item) => {
      arr.push((item.y + item.h) * scale);
      return arr;
    }, [] as any);
    dragBoxConfig.value.left = Math.min(...minXSet);
    dragBoxConfig.value.width = Math.max(...maxXSet) - Math.min(...minXSet);
    dragBoxConfig.value.top = Math.min(...minYSet);
    dragBoxConfig.value.height = Math.max(...maxYSet) - Math.min(...minYSet);

    /* 打开框选容器 */
    if (selectSet.value.size >= 2) {
      dragBoxConfig.value.show = true;
    } else {
      dragBoxConfig.value.show = false;
    }
    window.removeEventListener("mouseup", mouseup);
    window.removeEventListener("mousemove", mousemove);
  };
  window.addEventListener("mousemove", mousemove);
  window.addEventListener("mouseup", mouseup);
};

/**
 * 注释: 框选容器被按下,准备拖拽
 * 提示：逻辑复杂,尽量别优化改动
 *      变量不可解构,否则不能检测到动态改变的宽高
 *
 * */
const containerMousedown = (event: MouseEvent) => {
  const { left, width, height, top } = dragBoxConfig.value;
  const offsetList: any[] = Array.from(selectSet.value).map((id) => {
    const item = findShapeById(id) as any;
    return {
      x: item.x,
      y: item.y,
      ox: item.x - left / currentStage.value.scale,
      oy: item.y - top / currentStage.value.scale
    };
  });
  const mousemove = (evt: MouseEvent) => {
    const mx = evt.x - event.x;
    const my = evt.y - event.y;
    const ox = mx + left;
    const oy = my + top;

    dragBoxConfig.value.left = range(
      ox,
      0,
      currentStage.value.w * currentStage.value.scale - width
    );
    dragBoxConfig.value.top = range(
      oy,
      0,
      currentStage.value.h * currentStage.value.scale - height
    );

    const items = Array.from(selectSet.value);

    items.forEach((id, index) => {
      const current = findShapeById(id);
      const cx = mx / currentStage.value.scale + offsetList[index].x;
      const cy = my / currentStage.value.scale + offsetList[index].y;
      const minx = offsetList[index].ox;
      const miny = offsetList[index].oy;
      current.x = range(
        cx,
        minx,
        currentStage.value.w - width / currentStage.value.scale + offsetList[index].ox
      );
      current.y = range(
        cy,
        miny,
        currentStage.value.h - height / currentStage.value.scale + offsetList[index].oy
      );
    });
  };

  const mouseup = () => {
    window.removeEventListener("mouseup", mouseup);
    window.removeEventListener("mousemove", mousemove);
  };
  window.addEventListener("mousemove", mousemove);
  window.addEventListener("mouseup", mouseup);
};

/**
 * 注释: watch监听属性
 *
 * */

/* 监听框选内容 */
watch(selectSet.value, (value) => {
  if (value.size === 0) {
    dragBoxConfig.value.show = false;
  }
});
/**
 * 注释: 核心样式
 *
 * */
const style = computed(() => {
  return {
    width: `${Math.ceil(currentStage.value.w)}px`,
    height: `${Math.ceil(currentStage.value.h)}px`,
    left: `${offsetDom.value.left / currentStage.value.scale}px`,
    top: `${offsetDom.value.top / currentStage.value.scale}px`,
    zoom: currentStage.value.scale,
    backgroundColor: currentStage.value.backgroundColor
  };
});

const selectionStyle = computed(() => {
  return {
    left: `${selectionConfig.value.left / currentStage.value.scale}px`,
    top: `${selectionConfig.value.top / currentStage.value.scale}px`,
    width: `${selectionConfig.value.width / currentStage.value.scale}px`,
    height: `${selectionConfig.value.height / currentStage.value.scale}px`
  };
});

const containerStyle = computed(() => {
  return {
    left: `${dragBoxConfig.value.left / currentStage.value.scale}px`,
    top: `${dragBoxConfig.value.top / currentStage.value.scale}px`,
    width: `${dragBoxConfig.value.width / currentStage.value.scale}px`,
    height: `${dragBoxConfig.value.height / currentStage.value.scale}px`
  };
});
</script>

<style lang="scss" scoped>
.layout-content {
  color: var(--color-text-1);
  background: var(--color-bg-3);
  overflow: hidden;
  width: 100%;
}

.canvas {
  position: absolute;
  box-shadow: 0 0 5px gray;
}

// 框选样式
.selection,
.drag-container {
  outline: 1px solid var(--primary);
  background-color: #006eff15;
  position: fixed;
  z-index: 50;
}

.drag-container {
  position: absolute;
  z-index: 40;
}
</style>
